1 module core_4_5;
2 import std.experimental.xml.dom : Node;
3 import defs;
4 import util;
5 
6 enum IndexFile = "man4/html/index.php";
7 enum PrependXMLFileLocation = "man4/";
8 
9 OGLFunctionFamily[] readInFunctionFamilies() {
10 	import std.file : readText;
11 	import std.experimental.xml;
12 
13 	string raw_input = readText(IndexFile);
14 	OGLFunctionFamily[] ret;
15 
16 	auto domBuilder = raw_input
17 		.lexer
18 		.parser
19 		.cursor((CursorError err){})
20 		.domBuilder;
21 	domBuilder.setSource(raw_input);
22 	domBuilder.buildRecursive;
23 	auto dom = domBuilder.getDocument;
24 
25 	auto apiEntryPoints = dom.firstChild.childNodes.item(1).childNodes.item(1).firstChild.childNodes.item(2);
26 	auto level2 = apiEntryPoints.childNodes.item(1);
27 
28 	foreach(level3parent; level2.childNodes) {
29 		if (level3parent.nodeName != "li")
30 			continue;
31 		auto level3 = level3parent.childNodes.item(1);
32 		ret.length += level3.childNodes.length;
33 
34 		size_t i = level3.childNodes.length;
35 	F2: foreach(functag; level3.childNodes) {
36 			auto atag = functag.childNodes.item(0);
37 			char[] filename = cast(char[])atag.attributes.getNamedItem("href").nodeValue;
38 			string value = atag.firstChild.nodeValue;
39 
40 			switch(filename) {
41 				case "removedTypes.xhtml":
42 					ret.length--;
43 					i--;
44 					continue F2;
45 				default:
46 					filename[$-4] = 'm';
47 					filename[$-3] = 'l';
48 					filename = filename[0 .. $-2];
49 					break;
50 			}
51 
52 			filename = PrependXMLFileLocation ~ filename;
53 
54 			foreach(family; ret) {
55 				if (family.fromFilename == filename) {
56 					ret.length--;
57 					i--;
58 					continue F2;
59 				}
60 			}
61 
62 			ret[$-i] = OGLFunctionFamily(cast(string)filename, value);
63 			i--;
64 		}
65 	}
66 
67 	foreach(ref family; ret) {
68 		family.functions = family.readInFunctions;
69 	}
70 
71 	return ret;
72 }
73 
74 OGLFunction[] readInFunctions(ref OGLFunctionFamily family) {
75 	import std.file : readText;
76 	import std.conv : to;
77 	import std.experimental.xml;
78 
79 	string raw_input = readText(family.fromFilename);
80 	OGLFunction[] ret;
81 
82 	if (raw_input.length < 72) {
83 		return null;
84 	} else
85 		raw_input = raw_input[71 .. $];
86 
87 	auto domBuilder = raw_input
88 		.lexer
89 			.parser
90 			.cursor((CursorError err){})
91 			.domBuilder;
92 	domBuilder.setSource(raw_input);
93 	domBuilder.buildRecursive;
94 	auto dom = domBuilder.getDocument;
95 
96 	//
97 
98 	auto copyrightTags = dom.getElementsByTagName("copyright");
99 	if (copyrightTags !is null && copyrightTags.length > 0) {
100 		auto copyright = copyrightTags[0];
101 		family.copyright = copyright.lastChild.firstChild.nodeValue ~ " " ~ copyright.firstChild.firstChild.nodeValue;
102 	}
103 
104 	//
105 
106 	auto funcprototypes = dom.getElementsByTagName("funcsynopsis")[0].childNodes;
107 	ret.length = funcprototypes.length;
108 
109 	size_t i;
110 	foreach(funcprototype; funcprototypes) {
111 		switch(funcprototype.nodeName) {
112 			case "funcprototype":
113 				break;
114 			default:
115 				ret.length--;
116 				continue;
117 		}
118 
119 		ret[i].returnType = funcprototype.firstChild.firstChild.nodeValue.fixTypePointer;
120 		ret[i].name = funcprototype.firstChild.lastChild.firstChild.nodeValue;
121 
122 		ret[i].argNames.length = funcprototype.childNodes.length;
123 		ret[i].argTypes.length = ret[i].argNames.length;
124 
125 		size_t j;
126 		foreach(paramdef; funcprototype.childNodes) {
127 			switch(paramdef.nodeName) {
128 				case "paramdef":
129 					break;
130 				default:
131 					ret[i].argNames.length--;
132 					ret[i].argTypes.length--;
133 					continue;
134 			}
135 
136 			if (paramdef.firstChild.nodeValue.length > 0) {
137 				ret[i].argTypes[j] = paramdef.firstChild.nodeValue.fixTypePointer;
138 
139 				if (ret[i].argTypes[j].length > 0 && ret[i].argTypes[j][$-1] == ' ')
140 					ret[i].argTypes[j].length--;
141 
142 				if (paramdef.childNodes.length == 2)
143 					ret[i].argNames[j] = paramdef.lastChild.firstChild.nodeValue;
144 			} else
145 				ret[i].argTypes[j] = paramdef.firstChild.firstChild.nodeValue.fixTypePointer;
146 
147 			j++;
148 		}
149 
150 		i++;
151 	}
152 
153 	//
154 
155 	auto refsect1 = dom.getElementsByTagName("refsect1");
156 	foreach(node; refsect1) {
157 		if (node.attributes.getNamedItem("xml:id") is null)
158 			continue;
159 
160 		switch(node.attributes.getNamedItem("xml:id").nodeValue) {
161 			case "parameters":
162 				auto varlistentries = node.childNodes.item(1).childNodes;
163 				family.docs_parameters.length = varlistentries.length;
164 
165 				i = 0;
166 				foreach(varlistentry; varlistentries) {
167 					auto parameters = varlistentry.firstChild.childNodes;
168 					auto paras = varlistentry.lastChild.childNodes;
169 
170 					family.docs_parameters[i].appliesToNames.length = parameters.length;
171 
172 					size_t j;
173 					foreach(param; parameters) {
174 						if (param.nodeName == "#text") {
175 							family.docs_parameters[i].appliesToNames.length--;
176 							continue;
177 						}
178 						family.docs_parameters[i].appliesToNames[j] = param.firstChild.nodeValue;
179 						j++;
180 					}
181 
182 					foreach (para; paras)
183 						family.docs_parameters[i].documentation.evaluateDocs(para);
184 
185 					i++;
186 				}
187 				break;
188 
189 			case "description":
190 				i = 0;
191 				foreach(child; node.childNodes) {
192 					i++;
193 					if (i == 1)
194 						continue;
195 					family.docs_description.evaluateDocs(child);
196 				}
197 				break;
198 
199 			case "notes":
200 				i = 0;
201 				foreach(child; node.childNodes) {
202 					i++;
203 					if (i == 1)
204 						continue;
205 					family.docs_notes.evaluateDocs(child);
206 				}
207 				break;
208 
209 			case "seealso":
210 				i = 0;
211 				foreach(child; node.childNodes) {
212 					i++;
213 					if (i == 1)
214 						continue;
215 					family.docs_seealso.evaluateDocs(child);
216 				}
217 				break;
218 
219 			case "Copyright":
220 			case "copyright":
221 				i = 0;
222 				foreach(child; node.childNodes) {
223 					i++;
224 					if (i == 1)
225 						continue;
226 					family.docs_copyright.evaluateDocs(child);
227 				}
228 				break;
229 
230 			case "versions":
231 				// refsect1.informaltable.tgroup.tbody.trow.xi:include
232 				auto xiinclude = node.childNodes.item(1).childNodes.item(0).childNodes.item(1).childNodes.item(0).childNodes.item(1);
233 				string value = xiinclude.attributes.getNamedItem("xpointer").nodeValue;
234 
235 				// xpointer(/*/*[@role='20']/*)
236 				family.introducedIn = cast(OGLIntroducedIn)(value["xpointer(/*/*[@role='".length .. $][0 .. 2].to!ushort);
237 
238 				break;
239 
240 			case "errors":
241 				i = 0;
242 				foreach(child; node.childNodes) {
243 					i++;
244 					if (i == 1)
245 						continue;
246 					family.docs_errors.evaluateDocs(child);
247 				}
248 				break;
249 
250 			default:
251 				break;
252 		}
253 	}
254 
255 	//
256 
257 	return ret;
258 }
259 
260 void evaluateDocs(ref OGLDocumentation parentContainer, Node!string current) {
261 	OGLDocumentation next;
262 
263 	switch(current.nodeName) {
264 		case "para":
265 			next = OGLDocumentation(OGLDocumentationType.Paragraph);
266 			goto case "$$container$$";
267 		case "title":
268 			next = OGLDocumentation(OGLDocumentationType.Title);
269 			goto case "$$container$$";
270 
271 		case "constant":
272 			next = OGLDocumentation(OGLDocumentationType.LookupConstant);
273 			goto case "$$container$$";
274 		case "parameter":
275 			next = OGLDocumentation(OGLDocumentationType.LookupParameter);
276 			goto case "$$container$$";
277 		case "refentrytitle":
278 		case "function":
279 			next = OGLDocumentation(OGLDocumentationType.LookupFunction);
280 			goto case "$$container$$";
281 
282 		case "tgroup":
283 			import std.conv : to;
284 			next = OGLDocumentation(OGLDocumentationType.TableContainer);
285 			next.value_numcols = current.attributes.getNamedItem("cols").nodeValue.to!int;
286 			goto case "$$container$$";
287 		case "thead":
288 			next = OGLDocumentation(OGLDocumentationType.TableHeader);
289 			goto case "$$container$$";
290 		case "tbody":
291 			next = OGLDocumentation(OGLDocumentationType.TableBody);
292 			goto case "$$container$$";
293 		case "row":
294 			next = OGLDocumentation(OGLDocumentationType.TableRow);
295 			goto case "$$container$$";
296 		case "entry":
297 			next = OGLDocumentation(OGLDocumentationType.TableEntry);
298 			goto case "$$container$$";
299 
300 		case "superscript":
301 			next = OGLDocumentation(OGLDocumentationType.StyleSuperScript);
302 			goto case "$$container$$";
303 		case "subscript":
304 			next = OGLDocumentation(OGLDocumentationType.StyleSubScript);
305 			goto case "$$container$$";
306 
307 		case "term":
308 		case "table":
309 		case "informaltable":
310 			next = OGLDocumentation(OGLDocumentationType.Container);
311 			goto case "$$container$$";
312 
313 		case "itemizedlist":
314 			next = OGLDocumentation(OGLDocumentationType.IndexList);
315 			goto case "$$container$$";
316 		case "listitem":
317 			next = OGLDocumentation(OGLDocumentationType.IndexItem);
318 			goto case "$$container$$";
319 
320 		case "variablelist":
321 			next = OGLDocumentation(OGLDocumentationType.List);
322 			goto case "$$container$$";
323 		case "varlistentry":
324 			next = OGLDocumentation(OGLDocumentationType.ListItem);
325 			goto case "$$container$$";
326 
327 		case "trademark":
328 			if (current.attributes is null)
329 				break;
330 			if (current.attributes.getNamedItem("class").nodeValue == "copyright")
331 				next = OGLDocumentation(OGLDocumentationType.Copyright);
332 			else
333 				next = OGLDocumentation(OGLDocumentationType.Trademark);
334 			goto case "$$container$$";
335 		case "link":
336 			next = OGLDocumentation(OGLDocumentationType.Link);
337 			next.value_string = current.attributes.getNamedItem("xlink:href").nodeValue;
338 			goto case "$$container$$";
339 		case "ulink":
340 			next = OGLDocumentation(OGLDocumentationType.Link);
341 			next.value_string = current.attributes.getNamedItem("url").nodeValue;
342 			goto case "$$container$$";
343 
344 		case "citerefentry":
345 			next = OGLDocumentation(OGLDocumentationType.Container);
346 			goto case "$$container$$";
347 		case "footnote":
348 			next = OGLDocumentation(OGLDocumentationType.Footnote);
349 			goto case "$$container$$";
350 
351 		case "emphasis":
352 			if (current.attributes is null)
353 				break;
354 			auto roleAttribute = current.attributes.getNamedItem("role");
355 			if (roleAttribute !is null) {
356 				next = OGLDocumentation(OGLDocumentationType.StyleContainer, roleAttribute.nodeValue);
357 				goto case "$$container$$";
358 			} else {
359 				break;
360 			}
361 		case "code":
362 		case "programlisting":
363 			next = OGLDocumentation(OGLDocumentationType.StyleCode);
364 			goto case "$$container$$";
365 
366 		case "$$container$$":
367 			auto children = current.childNodes;
368 			foreach(childI; 0 .. children.length) {
369 				next.evaluateDocs(children.item(childI));
370 			}
371 
372 			parentContainer.value_children ~= next;
373 			break;
374 
375 		case "xi:include":
376 			string newfile = PrependXMLFileLocation ~ current.attributes.getNamedItem("href").nodeValue;
377 
378 			import std.file : readText;
379 			import std.experimental.xml;
380 
381 			string raw_input = readText(newfile);
382 			auto domBuilder = raw_input
383 				.lexer
384 				.parser
385 				.cursor((CursorError err){})
386 				.domBuilder;
387 			domBuilder.setSource(raw_input);
388 			domBuilder.buildRecursive;
389 			auto dom = domBuilder.getDocument;
390 
391 			next = OGLDocumentation(OGLDocumentationType.Container);
392 
393 			auto root = dom.lastChild;
394 			if (root.nodeName == "informaltable" || root.nodeName == "table") {
395 				auto children = root.childNodes;
396 
397 				foreach(childI; 0 .. children.length) {
398 					next.evaluateDocs(children.item(childI));
399 				}
400 
401 				parentContainer.value_children ~= next;
402 			} else {
403 				parentContainer.value_children ~= OGLDocumentation(OGLDocumentationType.Unknown, current.nodeName);
404 			}
405 
406 			break;
407 
408 		case "inlineequation":
409 			next = OGLDocumentation(OGLDocumentationType.InlineEquation);
410 			auto children = current.childNodes;
411 			foreach(childI; 0 .. children.length) {
412 				next.evaluateDocs(children.item(childI));
413 			}
414 
415 			parentContainer.value_children ~= next;
416 			break;
417 		case "mml:apply":
418 		case "informalequation":
419 		case "mml:math":
420 			parentContainer.evaluateDocs_MathML(current);
421 			break;
422 
423 
424 		case "#text":
425 			parentContainer.value_children ~= OGLDocumentation(OGLDocumentationType.Text, current.nodeValue);
426 			break;
427 
428 		case "#comment":
429 		case "colspec":
430 			break;
431 
432 		default:
433 			parentContainer.value_children ~= OGLDocumentation(OGLDocumentationType.Unknown, current.nodeName);
434 			break;
435 	}
436 }
437 
438 void evaluateDocs_MathML(ref OGLDocumentation parentContainer, Node!string current) {
439 	OGLDocumentation next;
440 
441 	string nodeName = current.nodeName;
442 	if (nodeName.length > 3 && nodeName[0 .. 4] == "mml:")
443 		nodeName = nodeName[4 .. $];
444 
445 	switch(nodeName) {
446 		case "mi":
447 			next = OGLDocumentation(OGLDocumentationType.MathML_MI);
448 			if (current.attributes !is null && current.attributes.getNamedItem("mathvariant") !is null)
449 				next.value_string = current.attributes.getNamedItem("mathvariant").nodeValue;
450 			goto case "$$container$$";
451 		case "mfenced":
452 			next = OGLDocumentation(OGLDocumentationType.MathML_mfenced);
453 			if (current.attributes.getNamedItem("open") !is null)
454 				next.value_string = current.attributes.getNamedItem("open").nodeValue;
455 			if (current.attributes.getNamedItem("close") !is null)
456 				next.value_string2 = current.attributes.getNamedItem("close").nodeValue;
457 			goto case "$$container$$";
458 		case "mn":
459 			next = OGLDocumentation(OGLDocumentationType.MathML_mn);
460 			goto case "$$container$$";
461 		case "mrow":
462 			next = OGLDocumentation(OGLDocumentationType.MathML_mrow);
463 			goto case "$$container$$";
464 		case "msup":
465 			next = OGLDocumentation(OGLDocumentationType.MathML_msup);
466 			goto case "$$container$$";
467 		case "mo":
468 			next = OGLDocumentation(OGLDocumentationType.MathML_mo);
469 			goto case "$$container$$";
470 		case "msub":
471 			next = OGLDocumentation(OGLDocumentationType.MathML_msub);
472 			goto case "$$container$$";
473 		case "mfrac":
474 			next = OGLDocumentation(OGLDocumentationType.MathML_mfrac);
475 			goto case "$$container$$";
476 		case "mtable":
477 			next = OGLDocumentation(OGLDocumentationType.MathML_mtable);
478 			if (current.attributes !is null && current.attributes.length > 0 && current.attributes.getNamedItem("columnalign") !is null)
479 				next.value_string = current.attributes.getNamedItem("columnalign").nodeValue;
480 			goto case "$$container$$";
481 		case "mtr":
482 			next = OGLDocumentation(OGLDocumentationType.MathML_mtr);
483 			goto case "$$container$$";
484 		case "mtd":
485 			next = OGLDocumentation(OGLDocumentationType.MathML_mtd);
486 			if (current.attributes !is null && current.attributes.length > 0 && current.attributes.getNamedItem("columnalign") !is null)
487 				next.value_string = current.attributes.getNamedItem("columnalign").nodeValue;
488 			goto case "$$container$$";
489 		case "mtext":
490 			next = OGLDocumentation(OGLDocumentationType.MathML_mtext);
491 			if (current.attributes !is null && current.attributes.length > 0 && current.attributes.getNamedItem("mathvariant") !is null)
492 				next.value_string = current.attributes.getNamedItem("mathvariant").nodeValue;
493 			goto case "$$container$$";
494 		case "apply":
495 			next = OGLDocumentation(OGLDocumentationType.MathML_apply);
496 			goto case "$$container$$";
497 		case "mover":
498 			next = OGLDocumentation(OGLDocumentationType.MathML_mover);
499 			goto case "$$container$$";
500 		case "munderover":
501 			next = OGLDocumentation(OGLDocumentationType.MathML_munderover);
502 			goto case "$$container$$";
503 		case "msqrt":
504 			next = OGLDocumentation(OGLDocumentationType.MathML_msqrt);
505 			goto case "$$container$$";
506 
507 		case "csymbol":
508 			next = OGLDocumentation(OGLDocumentationType.MathML_csymbol);
509 			goto case "$$container$$";
510 
511 		case "math":
512 			next = OGLDocumentation(OGLDocumentationType.MathMLContainer);
513 			goto case "$$container$$";
514 
515 		case "informalequation":
516 			auto children = current.childNodes;
517 			foreach(childI; 0 .. children.length) {
518 				parentContainer.evaluateDocs_MathML(children.item(childI));
519 			}
520 			break;
521 
522 		case "$$container$$":
523 			auto children = current.childNodes;
524 			foreach(childI; 0 .. children.length) {
525 				next.evaluateDocs_MathML(children.item(childI));
526 			}
527 
528 			parentContainer.value_children ~= next;
529 			break;
530 
531 		case "mspace":
532 			next = OGLDocumentation(OGLDocumentationType.MathML_mspace);
533 			next.value_string = current.attributes.getNamedItem("width").nodeValue;
534 			parentContainer.value_children ~= next;
535 			break;
536 
537 		case "floor":
538 			next = OGLDocumentation(OGLDocumentationType.MathML_floor);
539 			parentContainer.value_children ~= next;
540 			break;
541 		case "infinity":
542 			next = OGLDocumentation(OGLDocumentationType.MathML_infinity);
543 			parentContainer.value_children ~= next;
544 			break;
545 
546 		default:
547 			parentContainer.evaluateDocs(current);
548 			break;
549 	}
550 }